﻿using SqlSugar;
using System.Linq.Expressions;
using Wechat_PublicNumber.Common;
using Wechat_PublicNumber.Entity;
using Wechat_PublicNumber.Model;
using Wechat_PublicNumber.Model.Input.PublicNumberPushTemplate;

namespace Wechat_PublicNumber.Repository
{
    public class WxTemplateRepository : DataAccess
    {
        [Autowired]
        private CommonHttpInvoke _commonHttpInvoke;

        /// <summary>
        /// 获取模板
        /// </summary>
        /// <param name="tempID"></param>
        /// <param name="openID"></param>
        /// <param name="parm"></param>
        /// <returns></returns>
        public async Task<WxPushTemplate> GetTemplateData(WeChatTemplateEnum tempID, string openID, object parm = null)
        {
            return (await GetTemplateData(tempID, new List<string> { openID }, parm)).FirstOrDefault();
        }

        /// <summary>
        /// 获取模板
        /// </summary>
        /// <param name="tempID"></param>
        /// <param name="openIDList"></param>
        /// <param name="parm"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<List<WxPushTemplate>> GetTemplateData(WeChatTemplateEnum tempID, List<string> openIDList, object parm = null)
        {
            var tempData = await GetWeChatTemplateByID(tempID);
            if (tempData is null)
                throw new Exception("请配置WeChatTemplate");

            //如果是Admin的就只给Admin配置的OpenID推送
            if (tempData.Type == WeChatTemplateTypeEnum.Special)
            {
                openIDList = tempData.OpenID.Split(',').ToList();
            }

            #region 排除掉不需要发送的用户
            var closeUser = await WXDb.Queryable<WeChatTemplateSet>()
                .Where(s => s.TemplateID == tempID && !s.Status && openIDList.Contains(s.OpenID))
                .Select(s => s.OpenID)
                .ToListAsync();

            openIDList.RemoveAll(s => closeUser.Contains(s));
            #endregion

            if (openIDList.Count <= 0)
                return new List<WxPushTemplate>();

            List<TemplateList> templateData = null;

            switch (tempID)
            {
                case WeChatTemplateEnum.Test:
                    templateData = await GetTestTemplate(openIDList);
                    break;

                case WeChatTemplateEnum.Weather:
                    templateData = await GetWeatherTemplate(openIDList);
                    break;

                case WeChatTemplateEnum.StockEnd:
                    templateData = GetStockEndTemplate(openIDList, GetWeChatTemplateParm<StockEndTemplateInput>(parm));
                    break;

                case WeChatTemplateEnum.IISRestart:
                    templateData = GetIISRestartTemplate(openIDList);
                    break;
                case WeChatTemplateEnum.SupplementWeekday:
                    templateData = GetSupplementWeekdayTemplate(openIDList, GetWeChatTemplateParm<SupplementWeekdayTemplateInput>(parm));
                    break;
                case WeChatTemplateEnum.CommonMessage:
                    templateData = GetCommonMessageTemplate(openIDList, GetWeChatTemplateParm<CommonMessageTemplateInput>(parm));
                    break;
                case WeChatTemplateEnum.StockWarning:
                    templateData = GetStockWarningTemplate(openIDList, GetWeChatTemplateParm<StockWarningTemplateInput>(parm));
                    break;

                default: throw new Exception("暂无该WeChatTemplateEnum");
            }

            return ReturnWxTemp(tempData, templateData);

            throw new Exception("暂无该WeChatTemplate");
        }

        /// <summary>
        /// 根据ID获取微信模板
        /// </summary>
        /// <param name="ID"></param>
        /// <returns></returns>
        public async Task<WeChatTemplate> GetWeChatTemplateByID(WeChatTemplateEnum ID) =>
            await WXDb.Queryable<WeChatTemplate>().FirstAsync(s => s.ID == ID);

        /// <summary>
        /// 获取所有微信模板
        /// </summary>
        /// <returns></returns>
        public async Task<List<WeChatTemplate>> GetWeChatTemplateList() =>
            await WXDb.Queryable<WeChatTemplate>().ToListAsync();

        /// <summary>
        /// 根据唯一Code获取微信模板
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        public async Task<WeChatTemplate> GetWeChatTemplateByCode(string code) =>
            await WXDb.Queryable<WeChatTemplate, WeChatChatKeyWord>((t, p) => new object[]
             {
                JoinType.Left,t.ParameterID==p.ID
             }).Where((t, p) => p.KeyWord == code)
            .FirstAsync();

        /// <summary>
        /// 设置是否接收微信模板
        /// </summary>
        /// <param name="templateID"></param>
        /// <param name="status"></param>
        /// <param name="openID"></param>
        /// <returns></returns>
        public async Task SetWeChatTemplateStatus(WeChatTemplateEnum templateID, bool status, string openID)
        {
            var nowtime = DateTime.Now;

            var setData = await WXDb.Queryable<WeChatTemplateSet>().Where(s => s.OpenID == openID && s.TemplateID == templateID).FirstAsync();

            if (setData is null)
            {
                setData = new WeChatTemplateSet()
                {
                    OpenID = openID,
                    TemplateID = templateID,
                    CreateTime = nowtime,
                };
            }
            setData.Status = status;
            setData.UpdateTime = nowtime;

            if (setData.ID == 0)
                await WXDb.Insertable(setData).ExecuteCommandAsync();
            else
                await WXDb.Updateable(setData).ExecuteCommandAsync();
        }

        #region GetTemplate

        /// <summary>
        /// Test 模板
        /// </summary>
        /// <param name="openIDList"></param>
        /// <returns></returns>
        private async Task<List<TemplateList>> GetTestTemplate(List<string> openIDList)
        {
            var data = new ValueColor() { value = $"{await GetNowDayType()}   {await GetLatestHoliday()}", color = "#E61C1C" };

            return openIDList.Select(s => new TemplateList()
            {
                OpenID = s,
                Data = new { data }
            }).ToList();
        }

        /// <summary>
        /// Weather 模板
        /// </summary>
        /// <param name="openIDList"></param>
        /// <returns></returns>
        private async Task<List<TemplateList>> GetWeatherTemplate(List<string> openIDList)
        {
            var returnList = new List<TemplateList>();

            //date
            var date = new ValueColor()
            {
                value = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm").TrimStart('2')}   {GetCnWeek(DateTime.Now.DayOfWeek.ToString())}   {await GetNowDayType()}",
                color = "#2ADDD7"
            };

            //remark
            var remark = new ValueColor() { value = $"{await GetLatestHoliday()}", color = "#E61C1C" };

            foreach (var openID in openIDList)
            {
                try
                {

                    var userInfo = await WXDb.Queryable<Entity.UserInfo>().Where(s => s.OpenID == openID).FirstAsync();
                    if (userInfo is null)
                        continue;

                    GouldWeather_Lives liveWeather = null;

                    GouldWeather_Forecasts_Casts forecastWeather = null;

                    if (!string.IsNullOrEmpty(userInfo.AreaCode))
                        liveWeather = (await _commonHttpInvoke.GetGouldWeatherInfoByAreaCode(userInfo.AreaCode))?.lives?[0];

                    if (liveWeather is not null)
                    {
                        forecastWeather = (await _commonHttpInvoke.GetGouldWeatherInfoByAreaCode(userInfo.AreaCode, "all"))?.forecasts?[0]?.casts?[0];
                    }

                    returnList.Add(new TemplateList()
                    {
                        OpenID = openID,
                        Data = new
                        {
                            date,
                            remark = liveWeather is null ? new ValueColor() { value = "暂无法获取您的地理位置，请您授权允许使用地理位置", color = "#03E330" } : remark,
                            city = new ValueColor() { value = $"{liveWeather?.province} {liveWeather?.city}", color = "#03E330" },
                            weather = new ValueColor() { value = liveWeather?.weather, color = "#03E330" },
                            lowest = new ValueColor() { value = forecastWeather?.nighttemp, color = "#03E330" },
                            highest = new ValueColor() { value = forecastWeather?.daytemp, color = "#03E330" },
                            real = new ValueColor() { value = liveWeather?.temperature, color = "#03E330" }
                        }
                    });
                }
                catch (Exception ex)
                {
                    _logger.Error(ex, $"GetWeatherTemplate Error OpenID:{openID}");
                }
            }

            return returnList;
        }

        /// <summary>
        /// PushTemplateError 模板
        /// </summary>
        /// <param name="openIDList"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        private List<TemplateList> GetStockEndTemplate(List<string> openIDList, StockEndTemplateInput input)
        {
            var data = new
            {
                stock1 = new ValueColor() { value = input.stock1, color = "#C734DE" },
                stock2 = new ValueColor() { value = input.stock2, color = "#C734DE" },
                stock3 = new ValueColor() { value = input.stock3, color = "#C734DE" },
                stock4 = new ValueColor() { value = input.stock4, color = "#C734DE" },
                stock5 = new ValueColor() { value = input.stock5, color = "#C734DE" }
            };

            return openIDList.Select(s => new TemplateList()
            {
                OpenID = s,
                Data = data
            }).ToList();
        }

        /// <summary>
        /// IISRestart 模板
        /// </summary>
        /// <param name="openIDList"></param>
        /// <returns></returns>
        private List<TemplateList> GetIISRestartTemplate(List<string> openIDList)
        {
            var data = new
            {
                datetime = new ValueColor() { value = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss}", color = "#C734DE" }
            };

            return openIDList.Select(s => new TemplateList()
            {
                OpenID = s,
                Data = data
            }).ToList();
        }

        /// <summary>
        /// 假期补班提醒 模板
        /// </summary>
        /// <param name="openIDList"></param>
        /// <returns></returns>
        private List<TemplateList> GetSupplementWeekdayTemplate(List<string> openIDList, SupplementWeekdayTemplateInput input)
        {
            var data = new
            {
                holiday = new ValueColor() { value = input.Holiday, color = "#E61C1C" }
            };

            return openIDList.Select(s => new TemplateList()
            {
                OpenID = s,
                Data = data
            }).ToList();
        }

        /// <summary>
        /// 公共提醒模板
        /// </summary>
        /// <param name="openIDList"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        private List<TemplateList> GetCommonMessageTemplate(List<string> openIDList, CommonMessageTemplateInput input)
        {
            var data = new
            {
                message = new ValueColor() { value = input.Message, color = "#000000" }
            };

            return openIDList.Select(s => new TemplateList()
            {
                OpenID = s,
                Data = data
            }).ToList();
        }

        /// <summary>
        /// 股票预警
        /// </summary>
        /// <param name="openIDList"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        private List<TemplateList> GetStockWarningTemplate(List<string> openIDList, StockWarningTemplateInput input)
        {
            var data = new
            {
                stockName = new ValueColor() { value = input.stockName, color = "#FF0000" },
                stockprice = new ValueColor() { value = input.stockprice.ToString(), color = input.stockchg > 0 ? "#FF0000" : "#2FDA76" },
                stockchg = new ValueColor() { value = input.stockchg.ToString(), color = input.stockchg > 0 ? "#FF0000" : "#2FDA76" },
                stockpercent = new ValueColor() { value = input.stockpercent.ToString() + "%", color = input.stockchg > 0 ? "#FF0000" : "#2FDA76" },
            };

            return openIDList.Select(s => new TemplateList()
            {
                OpenID = s,
                Data = data
            }).ToList();
        }

        #endregion

        #region Private
        /// <summary>
        /// parmConvert
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="parm"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        private T GetWeChatTemplateParm<T>(object parm) where T : class, new()
        {
            if (parm is null)
                throw new Exception("parm不能为空");
            return parm.Convert<T>();
        }

        /// <summary>
        /// Return
        /// </summary>
        /// <param name="tempData"></param>
        /// <param name="tempList"></param>
        /// <returns></returns>
        private List<WxPushTemplate> ReturnWxTemp(WeChatTemplate tempData, List<TemplateList> tempList)
        {
            return tempList.Select(s => new WxPushTemplate
            {
                touser = s.OpenID,
                template_id = tempData.WeChatTemplateID,
                data = s.Data,
                Description = tempData.Description
            }).ToList();
        }

        /// <summary>
        /// 获取一周的中文
        /// </summary>
        /// <param name="EnWeek"></param>
        /// <returns></returns>
        private string GetCnWeek(string EnWeek)
        {
            switch (EnWeek)
            {
                case "Monday": return "星期一";
                case "Tuesday": return "星期二";
                case "Wednesday": return "星期三";
                case "Thursday": return "星期四";
                case "Friday": return "星期五";
                case "Saturday": return "星期六";
                case "Sunday": return "星期日";
                default: return EnWeek;
            }
        }

        /// <summary>
        /// 获取最近的节假日
        /// </summary>
        /// <returns></returns>
        private async Task<string> GetLatestHoliday()
        {
            var today = DateTime.Today;

            Expression<Func<Holiday, HolidayTime, bool>> expression = (h, ht) => ht.Year == today.Year;

            if (today >= DateTime.Parse($"{today.Year} 10-01"))
                expression = (h, ht) => ht.Year == today.Year + 1;

            var source = await WXDb.Queryable<Holiday, HolidayTime>((h, ht) => new object[]
             {
               JoinType.Left,h.ID==ht.HolidayID
             })
            .Where(expression)
            .Where((h, ht) => ht.Time >= today)
            .OrderBy((h, ht) => h.OrderBy)
            .OrderBy((h, ht) => ht.Type)
            .OrderBy((h, ht) => ht.Time)
            .Select((h, ht) => new
            {
                Holiday = h.ID,
                h.Name,
                ht.Type,
                ht.Time,
                ht.Year
            })
            .ToListAsync();

            if (source is null || source.Count <= 0)
                return "今年后面没节假日了哦！";

            var latestHoliday = source.Where(s => s.Type == HolidayTypeEnum.Holiday).ToList().First();

            if (latestHoliday.Time.Date == today)
            {
                source.RemoveAll(x => x.Holiday == latestHoliday.Holiday);
                latestHoliday = source.Where(s => s.Type == HolidayTypeEnum.Holiday).ToList().First();
            }

            return $"距离 {latestHoliday.Name} 放假还有 {(latestHoliday.Time.Date - today).Days}天";
        }

        /// <summary>
        /// 获取当天类型
        /// </summary>
        /// <returns></returns>
        private async Task<string> GetNowDayType()
        {
            var today = DateTime.Today;

            var data = await WXDb.Queryable<Holiday, HolidayTime>((h, ht) => new object[]
            {
               JoinType.Left,h.ID==ht.HolidayID
            })
            .Where((h, ht) => ht.Time == today)
            .Select((h, ht) => new
            {
                h.Name,
                ht.Type
            })
            .FirstAsync();

            if (data != null)
                return data.Name + data.Type.GetDescription();

            if (today.DayOfWeek.ToString() == "Saturday" || today.DayOfWeek.ToString() == "Sunday")
                return "休息日";
            else
                return "工作日";
        }

        private class TemplateList
        {
            public string OpenID { get; set; }

            public object Data { get; set; }
        }
        #endregion
    }
}
